package priv.abadstring.study.haoke.webapi.controller;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import priv.abadstring.study.haoke.webapi.es.House;
import priv.abadstring.study.haoke.webapi.vo.SearchResult;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * ElasticsearchController
 *
 * @author aBadString
 * @date 2021-08-09 18:14:25
 */
@RequestMapping("search")
@RestController
@CrossOrigin
public class ElasticsearchController {
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    public static final Integer ROWS = 10;

    @GetMapping
    public SearchResult search(@RequestParam("keyword") String keyWord,
                               @RequestParam(value = "page", defaultValue = "1") Integer page) {
        // 防止爬虫抓取过多的数据
        if(page > 100){
            page = 1;
        }

        PageRequest pageRequest = PageRequest.of(page - 1, ROWS);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("title", keyWord).operator(Operator.AND))
                .withPageable(pageRequest)
                // 设置关键词高亮
                .withHighlightFields(new HighlightBuilder.Field("title"))
                .build();
        AggregatedPage<House> housePage = elasticsearchTemplate.queryForPage(
                searchQuery, House.class, searchResultMapper());
        return new SearchResult(housePage.getTotalPages(), housePage.getContent());



    }

    private SearchResultMapper searchResultMapper() {
         return new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                List<T> result = new ArrayList<>();
                if (response.getHits().totalHits == 0) {
                    return new AggregatedPageImpl<>
                            (Collections.emptyList(), pageable, 0L);
                }
                for (SearchHit searchHit : response.getHits()) {
                    // 通过反射写入数据到对象中
                    T obj = (T) ReflectUtils.newInstance(clazz);
                    try {
                        //写入id
                        FieldUtils.writeField(obj, "id",
                                searchHit.getId(), true);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    Map<String, Object> sourceAsMap =
                            searchHit.getSourceAsMap();
                    for (Map.Entry<String, Object> entry :
                            sourceAsMap.entrySet()) {
                        Field field = FieldUtils.getField(clazz,
                                entry.getKey(), true);
                        if (null == field) {
                            continue;
                        }
                        try {
                            FieldUtils.writeField(obj, entry.getKey(),
                                    entry.getValue(), true);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                    // 处理高亮
                    for (Map.Entry<String, HighlightField> stringHighlightFieldEntry : searchHit.getHighlightFields().entrySet()) {
                        try {
                            Text[] fragments = stringHighlightFieldEntry.getValue().fragments();
                            StringBuilder sb = new StringBuilder();
                            for (Text fragment : fragments) {
                                sb.append(fragment.toString());
                            }
                            FieldUtils.writeField(obj,
                                    stringHighlightFieldEntry.getKey(), sb.toString(), true);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                    result.add(obj);
                }
                return new AggregatedPageImpl<>(result, pageable, response.getHits().totalHits);
            }
        };
    }
}
